#pragma once 

#include <string>
#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define PATH "."
#define FILENAME "fifo"

#define ERR_EXIT(str)           \
    do                          \
    {                           \
        perror(str);            \
        exit(EXIT_FAILURE);     \
    } while (false)

class NameFifo
{
public:

    NameFifo(const std::string& path, const std::string& name)
        :_path(path)
        ,_name(name)
        ,_fifopath(path + "/" + name)
    {
        umask(000);
        int n = mkfifo(_fifopath.c_str(), 0666);               // 创建管道文件

        if (n < 0)
        {
            ERR_EXIT("mkfifo");
        }
        std::cout << "mkfifo success" << std::endl;
    }

    ~NameFifo()
    {
        int n = unlink(_fifopath.c_str());
        
        if (n < 0)
        {
            ERR_EXIT("unlink");
        }
        std::cout << "unlink fifo success" << std::endl;
    }

private:

    std::string _path;
    std::string _name;
    std::string _fifopath;

};

class FileOper
{
public:

    FileOper(const std::string& path, const std::string& name)
        :_path(path)
        ,_name(name)
        ,_fifopath(path + "/" + name)
        ,_fd(-1)
    {

    }

    bool openToRead()
    {
        _fd = open(_fifopath.c_str(), O_RDONLY);           // client 未打开管道时
        if (_fd < 0)                                       // server 在这会处于阻塞状态
        {
            ERR_EXIT("open");
        }
        std::cout << "open success" << std::endl;

        return true;
    }

    int readFile()
    {
        int code = 0;
        int num = read(_fd, &code, sizeof(code));
        if (num < 0)
        {
            ERR_EXIT("read");
        }

        return num;
    }

    void closeFile()
    {
        close(_fd);
    }

    bool openToWrite()
    {
        _fd = open(_fifopath.c_str(), O_WRONLY);           // 需要 server 程序先运行创建管道文件才能正常运行
        if (_fd < 0)
        {
            ERR_EXIT("opent");
        }
        std::cout << "open success" << std::endl;

        return true;
    }

    void writeFile()
    {
        int code = 0;
        write(_fd, &code, sizeof(code));
    }

    ~FileOper()
    {
        ;
    }

private:

    std::string _path;
    std::string _name;
    std::string _fifopath;
    int _fd;
    
};

class Server
{
public:

    Server(const std::string& path, const std::string& name)
        :_nameFifo(path, name)
        ,_fileOper(path, name)
    {
        _fileOper.openToRead();
    }

    int receive()
    {
        return _fileOper.readFile();
    }

    ~Server()
    {
        _fileOper.closeFile();
    }

private:

    NameFifo _nameFifo;
    FileOper _fileOper;
};

class Client
{
public:

    Client(const std::string& path, const std::string& name)
        :_fileOper(path, name)
    {
        _fileOper.openToWrite();
    }

    void send()
    {
        _fileOper.writeFile();
    }

    ~Client()
    {
        _fileOper.closeFile();
    }

private:

    FileOper _fileOper;
};
